#include "Channel.h"
#include "Eventloop.h"
#include "log/Logger.h"
#include "utility/assertion.h"
#include <sstream>


Channel::Channel(Eventloop *loop, int fd):
    _loop{loop},
    _fd{fd},
    _events{0},
    _revents{0},
    _index{-1},
    _logHup{true},
    _tied{false},
    _eventHandling{false} 
{

}

Channel::~Channel() {
    assert(!_eventHandling);
}

void Channel::tie(std::shared_ptr<void> &obj) {
    _tie = obj;
    _tied = true;
}

void Channel::remove() {
    assert(is_non_event());
    _loop->remove_channel(this);
}

void Channel::_update() {
    _loop->update_channel(this);
}

void Channel::handle_event(Utility::timepoint recvTime) {
    std::shared_ptr<void> guard;
    if (_tied) {
        guard = _tie.lock();
        if (guard) {
            _handle_event_with_guard(recvTime);
        }
    }
    else {
        _handle_event_with_guard(recvTime);
    }
}

void Channel::_handle_event_with_guard(Utility::timepoint recvTime) {
    _eventHandling = true;
    if ( (_revents & POLLHUP) && !(_revents & POLLIN) ) {
        if (_logHup) {
            LOG_WARN << "Channel::handle_event() POLLHUP";
        }
        if (_closeCallback) {
            _closeCallback();
        }
    }
    if (_revents & POLLNVAL) {
        LOG_WARN << "Channel::handle_event() POLLNVAL";
    }
    if (_revents & (POLLERR | POLLNVAL)) {
        if (_errorCallback) {
            _errorCallback();
        }
    }
    if (_revents & (POLLIN | POLLPRI | POLLHUP)) {
        if (_readCallback) {
            _readCallback(recvTime);
        }
    }
    if (_revents & POLLOUT) {
        if (_writeCallback) {
            _writeCallback();
        }
    }
    _eventHandling = false;
}

std::string Channel::revents_to_string() const {
    std::ostringstream oss;
    oss << _fd << ": ";
    if (_revents & POLLIN)
        oss << "IN ";
    if (_revents & POLLPRI)
        oss << "PRI ";
    if (_revents & POLLOUT)
        oss << "OUT ";
    if (_revents & POLLHUP)
        oss << "HUP ";
    if (_revents & POLLRDHUP)
        oss << "RDHUP ";
    if (_revents & POLLERR)
        oss << "ERR ";
    if (_revents & POLLNVAL)
        oss << "NVAL ";
    
    return oss.str();
}